/* actions.h
 *
 * Copyright 2007-2008 OpenMoko, Inc.
 * Authored by Tick <tick@openmoko.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <actions.h>
#include <gears.h>
#include <debug.h>
#include <string.h>
#include <browser.h>
#include <packagekit/pk-enum.h>

extern Ecore_Evas * ee;
struct Installer_Progressbar *_AP_;

void remove_action_progressbar() {
    if (_AP_) {
        free_Progressbar(_AP_);
        _AP_ = NULL;
    }
}
static char * progress_bar_type_text (enum ActionType type) {
    if (type == INSTALL ||
            type == UPDATE) {
        return "Downloading ";
    } else if (type == UNINSTALL) {
        return "Uninstalling ";

    } else {
        return "";
    }
}

static void pk_update_progress_cb (void *data, DBusMessage *message);
static void pk_change_status_cb (void *data, DBusMessage *message);
static void pk_error_cb (void *data, DBusMessage *message);
static void pk_message_cb (void *data, DBusMessage *message);
static void __delayed_date_out_cb(void *data, DBusMessage *message);
static void refresh_and_retry_finished_cb(void *data, DBusMessage *message);

void action(struct Target *target) {
    Pk_Operation * pko;
    Evas *evas = ecore_evas_get(ee);

    if (!target) {
        error("What is your target!!\n");
        return;
    }
    pko = pko_new();

    pko->payload = target;

    // Adding general call back here
    pko->Operations[ProgressChanged_sig] = 	&pk_update_progress_cb;
    pko->Operations[StatusChanged_sig] = 	&pk_change_status_cb;
    pko->Operations[ErrorCode_sig] = 	&pk_error_cb;
    pko->Operations[Message_sig] = 		&pk_message_cb;
    pko->Operations[Finished_sig] = 	&__delayed_date_out_cb;


    _AP_ = new_Progressbar(evas, progress_bar_type_text(target->type));

    target->status = INITIAL;
    if (target->type == INSTALL) {
        debug("Do install!\n");
        pk_install_package(pko, target->package);
    } else if (target->type == UPDATE) {
        debug("Do update!\n");
        if (!strcmp(target->package, "+ Update all")) {
            pk_update_system(pko);
        } else {
            pk_update_package(pko, target->package);
        }
    } else if (target->type == UNINSTALL) {
        debug("Do uninstall!\n");
        pk_uninstall_package(pko, target->package);
    } else {
        error("Hey!! Bad type %d!!\n", target->type);
        free(pko);
        remove_action_progressbar();
        return;
    }
}


void refresh_and_retry(struct Target *target) {
    struct Target * new_target = (struct Target *) malloc(sizeof(struct Target));
    memmove(new_target, target, sizeof(struct Target));
    new_target->status = INITIAL;
    new_target->message = NULL;
    Pk_Operation * pko;
    Evas *evas = ecore_evas_get(ee);


    pko = pko_new();

    pko->payload = new_target;

    pko->Operations[ProgressChanged_sig] = 	&pk_update_progress_cb;
    pko->Operations[Finished_sig] = 	&refresh_and_retry_finished_cb;

    if (!_AP_) {
        _AP_ = new_Progressbar(evas, progress_bar_type_text(target->type));
    }
    pk_refresh_system(pko);
}

void retry_action(struct Target *target) {
    struct Target * new_target = (struct Target *) malloc(sizeof(struct Target));
    memmove(new_target, target, sizeof(struct Target));
    new_target->status = INITIAL;
    new_target->message = NULL;
    new_target->retry = !!1;
    action(new_target);
}

static void pk_update_progress_cb (void *data, DBusMessage *message) {
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Target * target = (struct Target *)pko->payload;
    DBusError _err;
    unsigned int percentage, subpercentage, elapsed, remaining;
    debug("get progress cb!!\n");
    pko_timeout_check(pko);
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_UINT32, &percentage,
                               DBUS_TYPE_UINT32, &subpercentage,
                               DBUS_TYPE_UINT32, &elapsed,
                               DBUS_TYPE_UINT32, &remaining,
                               DBUS_TYPE_INVALID)) {
        error("Error Message: %s\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    target->progress = percentage;
    debug("Progress: %d %d %d %d\n", percentage, subpercentage, elapsed, remaining);
    if (_AP_) {
        debug("Status is %d\n", target->status);
        if (target->progress > 100) {
            progressbar_animator_start(_AP_);
        } else {
            progressbar_animator_stop(_AP_);
            progressbar_update(_AP_, target->progress);
        }
    }
    put_a_mask_on_screen();
}

static int progressbar_delay_die(void *data) {
    struct Target * target = (struct Target *)data;
    if (target) {
        target_free(target);
    }
    remove_action_progressbar();
    scroll_page_back_to_root();
    return 0;
}

//pk_marshal_VOID__STRING_STRING change_status
static void pk_change_status_cb (void *data, DBusMessage *message) {
    char *status_text;
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Target * target = (struct Target *)pko->payload;
    DBusError _err;
    pko_timeout_check(pko);
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &status_text,
                               DBUS_TYPE_INVALID )) {
        error("Error Message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    debug("Status Changed: %s\n", status_text);
    if (!_AP_) {
        error("_AP_ is NULL!!\n");
        return;
    }
    put_a_mask_on_screen();
    if (target->status == FAILED) {
        progressbar_animator_stop(_AP_);
        progressbar_update(_AP_, 100);
        progressbar_text_set(_AP_, "Failed");
        return;
    }
    if (pk_status_enum_from_text(status_text) == PK_STATUS_ENUM_FINISHED) {
        if (target->status != FAILED) {
            target->status = SUCCESS;
        }
        progressbar_animator_stop(_AP_);
        progressbar_update(_AP_, 100);
        if (target->status == SUCCESS) {
            progressbar_text_set(_AP_, "Done ");
        } else {
            progressbar_text_set(_AP_, "Failed ");
        }
    } else if (target->type == INSTALL) {
        if (pk_status_enum_from_text(status_text) == PK_STATUS_ENUM_DOWNLOAD) {
            target->status = DOWNLOADING;
            progressbar_animator_stop(_AP_);
            progressbar_text_set(_AP_, "Downloading ");
        } else if (pk_status_enum_from_text(status_text) == PK_STATUS_ENUM_INSTALL) {
            if (target->status == INITIAL)
                return;
            target->status = INSTALLING;
            progressbar_animator_start(_AP_);
            if (target->progress)
                progressbar_text_set(_AP_, "Installing ");
        } else if (pk_status_enum_from_text(status_text) == PK_STATUS_ENUM_SETUP) {
            target->status = INITIAL;
            progressbar_animator_start(_AP_);
            progressbar_text_set(_AP_, "Preparing ");
        } else {
            error("Hun? %s!!\n", status_text);
        }
    } else if (target->type == UPDATE) {
        if (pk_status_enum_from_text(status_text) == PK_STATUS_ENUM_DOWNLOAD) {
            target->status = DOWNLOADING;
            progressbar_animator_stop(_AP_);
            progressbar_text_set(_AP_, "Downloading ");
        } else if (pk_status_enum_from_text(status_text) == PK_STATUS_ENUM_UPDATE) {
            if (target->status == INITIAL)
                return;
            target->status = UPDATING;
            progressbar_animator_start(_AP_);
            if (target->progress)
                progressbar_text_set(_AP_, "Updating ");
        } else if (pk_status_enum_from_text(status_text) == PK_STATUS_ENUM_SETUP) {
            target->status = INITIAL;
            progressbar_animator_start(_AP_);
            progressbar_text_set(_AP_, "Preparing ");
        } else {
            error("Hun? %s!!\n", status_text);
        }
    } else if (target->type == UNINSTALL) {
        progressbar_animator_start(_AP_);
    }
}


static void pk_error_cb (void *data, DBusMessage *message) {
    char *_message;
    char *detail;
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Target * target = (struct Target *)pko->payload;
    DBusError _err;

    pko_timeout_check(pko);
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &_message,
                               DBUS_TYPE_STRING, &detail,
                               DBUS_TYPE_INVALID
                              )) {
        error("Trans: %s Error Message: '%s'\n", pko->tid, _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    target->status = FAILED;
    if (target->message) {
        free(target->message);
    }
    target->message = strdup(_message);
    progressbar_text_set(_AP_, _message);
    debug("Trans: %s Error %s '%s'\n", pko->tid, _message, detail);
}

static void pk_message_cb (void *data, DBusMessage *message) {
    char * message_code;
    char *detail;
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Target * target = (struct Target *)pko->payload;
    DBusError _err;
    pko_timeout_check(pko);
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &message_code,
                               DBUS_TYPE_STRING, &detail,
                               DBUS_TYPE_INVALID
                              )) {
        error("Error Message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    if (!target->message) {
        target->message = strdup(message_code);
    }
    progressbar_text_set(_AP_, message_code);
    debug("Trans: %s message_code is %s '%s'\n", pko->tid, message_code, detail);
}

static void __delayed_date_out_cb(void *data, DBusMessage *message) {
    char *exit_text;
    unsigned int runtime;
    char buf[128];
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Target * target = (struct Target *)pko->payload;
    DBusError _err;
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &exit_text,
                               DBUS_TYPE_UINT32, &runtime,
                               DBUS_TYPE_INVALID )) {
        error("Error Message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    debug("Transaction '%s' is finished!\n", pko->tid);
    debug("exit_text: %s\n", exit_text);
    debug("runtime %d\n", runtime);

    pk_default_get_finished_cb(pko, message);
    if (target->status == SUCCESS && !target->need_refresh) {
        refresh_categories_lists();
        ecore_timer_add(3, &progressbar_delay_die, target);
    } else {
        buf[0] = '\0';
        remove_action_progressbar();
        if (target->need_refresh && !target->retry) {
            debug("refresh only\n");
            target->retry = !!0;
            refresh_and_retry(target);
        } else if (target->message) {
            debug("target->message is '%s'\n", target->message);
            if (!strcmp(target->message, "package-already-installed")) {
                snprintf(buf, sizeof(buf), "%s", "This package is already installed.");
            } else if (!strcmp(target->message, "package-download-failed") ||
                       !strcmp(target->message, "no-network")) {
                snprintf(buf, sizeof(buf), "%s", "Download package failed.");
            } else if (!strcmp(target->message, "repo-not-found")) {
                snprintf(buf, sizeof(buf), "%s", "Cannot find repository.");
            } else if (!strcmp(target->message, "package-not-found")) {
                snprintf(buf, sizeof(buf), "%s", "Cannot find package in repository.");
            } else if (!strcmp(target->message, "package-corrupt") && !target->retry) {
                target->retry = !!1;
                // need retry
                debug("refresh and retry\n");
                refresh_and_retry(target);
            } else {
                snprintf(buf, sizeof(buf), "Failed. Please try again. <br>The error message is '%s'.", target->message);
            }
        }
        target_free(target);
        if (strlen(buf) > 0) {
            message_alert(buf, NORMAL_ERROR, NULL);
        }
    }
    return;
}

static void refresh_and_retry_finished_cb(void *data, DBusMessage *message) {
    char *exit_text;
    unsigned int runtime;
    char buf[64];
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Target * target = (struct Target *)pko->payload;
    DBusError _err;
    pko_timeout_check(pko);
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &exit_text,
                               DBUS_TYPE_UINT32, &runtime,
                               DBUS_TYPE_INVALID )) {
        error("Error Message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    debug("Transaction '%s' is finished!\n", pko->tid);
    debug("exit_text: %s\n", exit_text);
    debug("runtime %d\n", runtime);
    snprintf(buf, sizeof(buf), "<b><font_size=24>%s</></b>", exit_text);
    progressbar_text_set(_AP_, exit_text);


    pk_default_get_finished_cb(pko, message);
    buf[0] = '\0';
    remove_action_progressbar();
    if (target->message) {
        if (!strcmp(target->message, "package-already-installed")) {
            snprintf(buf, sizeof(buf), "%s", "This package has already installed.");
        } else if (!strcmp(target->message, "package-download-failed")) {
            snprintf(buf, sizeof(buf), "%s", "Download package failed.");
        } else {
            snprintf(buf, sizeof(buf), "%s", target->message);
        }
        debug("buf = %s\n", buf);
    }

    if (strlen(buf) > 0) {
        message_alert(buf, NORMAL_ERROR, NULL);
    } else if (target->retry) {
        debug("retrying\n");
        retry_action(target);
        target_free(target);
    } else {  // only refresh but not retry
        debug("refresh categories and slide to root\n");
        refresh_categories_lists();
        ecore_timer_add(3, &progressbar_delay_die, target);
    }
    return;
}
